package org.kth.dks.dks_dht;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.kth.dks.DKSObject;

public class DHTMemoryStorage implements DHTStorage{

	private Logger log = Logger.getLogger(DHTMemoryStorage.class);
	
  protected Map localHT;

  public DHTMemoryStorage() {
    localHT = new HashMap();
  }

  public synchronized void insertItem(long key, DKSObject value) {
    Long Key = new Long(key);
    if (localHT.get(Key)==null) {
      localHT.put(Key, new HashSet());
    }
    Set values = (Set) localHT.get(Key);
    values.add(value);
  }


  public synchronized DKSObject[] lookupItem(long key) {
    Long Key = new Long(key);
    Set values = (Set)localHT.get(Key);
    if (values==null || values.isEmpty())
      return new DKSObject[0];

    try{

      DKSObject [] dksObjArr = (DKSObject[]) values.toArray(new DKSObject[0]);
      return dksObjArr;

    }catch(NullPointerException e){
	return null;
    }catch(Exception e){
	log.error( "Caught exception: "+e.toString());
	e.printStackTrace();
	System.exit(-1);
    }
    return null;
  }

  public synchronized void removeItem(long key, DKSObject value) {
    Long Key = new Long(key);
    Set values = (Set)localHT.get(Key);
    if (values==null)
      return;
    //values.remove(value);//does NOT work!

    //So, we have to do like this:
    for (Iterator iter = values.iterator(); iter.hasNext();) {
      DKSObject dksobj = (DKSObject) iter.next();
      if( Arrays.equals( value.getData(), dksobj.getData() ) ) {
        iter.remove();
      }
    }

    if (values.isEmpty())
      localHT.remove(Key);
  }

  public synchronized void changeItem(long key, DKSObject oldvalue, DKSObject newvalue) {
    Long Key = new Long(key);
    Set values = (Set)localHT.get(Key);

    Iterator iter = values.iterator();
    while( iter.hasNext() ) {
      DKSObject dksobj = (DKSObject) iter.next();
      if( Arrays.equals( oldvalue.getData(), dksobj.getData() ) ) {
        values.remove( dksobj );
        values.add( newvalue );
      }
    }
  }

    /*FIXME!!! There must be a prettier way to do this...*/
    public synchronized long[] getAllKeys(){

	Set s=localHT.keySet();
	Long[] v=new Long[0];
	v=(Long [])s.toArray(v);
       	long [] v2=new long[v.length];
	for(int i=0;i<v.length;i++)
	    v2[i]=v[i].longValue();
	return v2;
    }

    public synchronized List getAllItems(int r){
	List entries = new LinkedList();
	Iterator iter = localHT.entrySet().iterator();
	while(iter.hasNext()){
	    Map.Entry entry = (Map.Entry) iter.next();
	    Long key = (Long) entry.getKey();
	    Iterator valueIter = ((Set)entry.getValue()).iterator();
	    while(valueIter.hasNext()){
		DKSObject oo = (DKSObject) valueIter.next();
		entries.add(new StoreTriplet(r, key, oo));
	    }
	}
	return entries;
    }

    public synchronized Iterator getEntriesIterator(){
	return localHT.entrySet().iterator();
    }
}
